﻿// Fill out your copyright notice in the Description page of Project Settings.


#include "MyCharacter.h"
#include "Camera\CameraComponent.h"
#include "GameFramework\SpringArmComponent.h"
#include "GameFramework\CharacterMovementComponent.h"
#include "Weapon.h"
#include "Animation\AnimInstance.h"
#include "DrawDebugHelpers.h"
#include "Engine\Engine.h"
#include "Components\SphereComponent.h"
#include "Enemy.h"
#include "Kismet\KismetMathLibrary.h"
#include "MySaveGame.h"
#include "MyPlayerController.h"
#include "Kismet\GameplayStatics.h"
#include "Kismet\KismetSystemLibrary.h"
#include "Components\CapsuleComponent.h"
#include "NiagaraSystem.h"
#include "NiagaraComponent.h"
#include "NiagaraFunctionLibrary.h"
#include "GameFramework\Controller.h"

// Sets default values
AMyCharacter::AMyCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	//结构体里写初始化工作
	BaseTurnRate = 65;
	BaseLookupRate = 65;

	SpringArmComb = CreateDefaultSubobject<USpringArmComponent>(TEXT("SprintArmComponent"));
	SpringArmComb->SetupAttachment(RootComponent);
	SpringArmComb->TargetArmLength = 400;
	SpringArmComb->bUsePawnControlRotation = true;

	CameraComb = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
	CameraComb->SetupAttachment(SpringArmComb,USpringArmComponent::SocketName);
	CameraComb->bUsePawnControlRotation = false;

	//Niagara
	NiagaraComponent = CreateDefaultSubobject<UNiagaraComponent>(TEXT("NiagaraComponent"));
	NiagaraComponent->SetupAttachment(RootComponent);

	bUseControllerRotationYaw = false;
	bUseControllerRotationPitch = false;
	bUseControllerRotationRoll = false;

	GetCharacterMovement()->bOrientRotationToMovement = true;
	GetCharacterMovement()->RotationRate = FRotator(0.f,540,0);
	GetCharacterMovement()->JumpZVelocity = 650;
	GetCharacterMovement()->AirControl = 0.2f;
	GetCharacterMovement()->MaxWalkSpeed = 600;

	//状态初始化
	Health = 100;
	MaxHealth = 100;
	Stamina = 150;
	MaxStamina = 150;
	SprintSpeed = 1000;
	RunSpeed = 600;
	IsExhausted = false;
	StaminaConsumeRate = 5;
	StaminaGrowthRate = 3;
	CurrentStatus = EStatus::ES_Normal;

	//攻击朝向
	CombatSphere = CreateDefaultSubobject<USphereComponent>(TEXT("CombatSphere"));
	CombatSphere->SetupAttachment(RootComponent);
	CombatSphere->SetSphereRadius(100);

	bReadyRoll = true;
	bClickRoll = false;
}

// Called when the game starts or when spawned
void AMyCharacter::BeginPlay()
{
	Super::BeginPlay();
	
	//开始游戏时，获取控制器的实例
	PlayerController = Cast<AMyPlayerController>(GetController());

	//动态绑定碰撞事件
	CombatSphere->OnComponentBeginOverlap.AddDynamic(this, &AMyCharacter::CombatSphereOverlapBegin);
	CombatSphere->OnComponentEndOverlap.AddDynamic(this, &AMyCharacter::CombatSphereOverlapEnd);
	//设置碰撞通道
	CombatSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
	CombatSphere->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);
}

// Called every frame
void AMyCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	float StaminaConsume = StaminaConsumeRate * DeltaTime;
	float StaminaGrowth = StaminaGrowthRate * DeltaTime;
	switch (CurrentStatus)
	{
	case EStatus::ES_Normal: 
	{
		float CurrentStamina = Stamina + StaminaGrowth;
		if (CurrentStamina < MaxStamina)
		{
			Stamina = CurrentStamina;
		}
		else
		{
			Stamina = MaxStamina;
			IsExhausted = false;
		}
		break;
	}
	case EStatus::ES_Sprint: 
	{
		float CurrentStamina = Stamina - StaminaConsume;
		if (CurrentStamina <= 0)
		{
			Stamina = 0;
			IsExhausted = true;
			SetStatus(EStatus::ES_Normal);
		}
		else
		{
			Stamina = CurrentStamina;
		}
		break;
	}
	default:
		break;
	}

	//每帧更新Enemy位置
	if (PlayerController && TargetEnemy)
	{
		//在角色处对Enemy血条位置更新
		PlayerController->UpdateEnemyHealthBarPosition(TargetEnemy->GetActorLocation());
	}

}

// Called to bind functionality to input
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	PlayerInputComponent->BindAxis("MoveForward",this,&AMyCharacter::MoveForward);
	PlayerInputComponent->BindAxis("MoveRight",this,&AMyCharacter::MoveRight);
	PlayerInputComponent->BindAxis("Lookup",this,&AMyCharacter::LookAtRate);
	PlayerInputComponent->BindAxis("Turn",this,&AMyCharacter::TurnAtRate);

	PlayerInputComponent->BindAction("Jump",IE_Pressed,this,&AMyCharacter::Jump);
	PlayerInputComponent->BindAction("Jump",IE_Released,this,&AMyCharacter::StopJumping);
	PlayerInputComponent->BindAction("Sprint",IE_Pressed,this,&AMyCharacter::BeginSprint);
	PlayerInputComponent->BindAction("Sprint",IE_Released,this,&AMyCharacter::EndSprint);
	PlayerInputComponent->BindAction("EquipWeapon",IE_Pressed,this,&AMyCharacter::SweapWeapon);
	PlayerInputComponent->BindAction("Click",IE_Pressed,this,&AMyCharacter::OnClickBegin);
	PlayerInputComponent->BindAction("Click",IE_Released,this,&AMyCharacter::OnClickEnd);
	PlayerInputComponent->BindAction("Pause",IE_Released,this,&AMyCharacter::PauseGame);
	PlayerInputComponent->BindAction("Roll",IE_Pressed,this,&AMyCharacter::ClickRollKey);
	PlayerInputComponent->BindAction("Roll",IE_Released,this,&AMyCharacter::UnClickRollKey);
	PlayerInputComponent->BindAction("Parry",IE_Pressed,this,&AMyCharacter::ClickParryKeyBegin);
	PlayerInputComponent->BindAction("Parry",IE_Released,this,&AMyCharacter::ClickParryKeyEnd);
}

void AMyCharacter::MoveForward(float value) 
{
	if (!bAttacking && !bReadyParry) 
	{
		if (Controller && value != 0)
		{
			const FRotator Rotation = Controller->GetControlRotation();
			const FRotator YawRotation(0.f, Rotation.Yaw, 0);
			const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
			AddMovementInput(Direction, value);
		}
	}	
}

void AMyCharacter::MoveRight(float value) 
{
	if (!bAttacking && !bReadyParry)
	{
		if (Controller && value != 0)
		{
			const FRotator Rotation = Controller->GetControlRotation();
			const FRotator YawRotation(0.f, Rotation.Yaw, 0);
			const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
			AddMovementInput(Direction, value);
		}
	}
}

void AMyCharacter::TurnAtRate(float value) 
{
	AddControllerYawInput(value * BaseTurnRate * GetWorld()->DeltaTimeSeconds);
}

void AMyCharacter::LookAtRate(float value) 
{
	AddControllerPitchInput(value * BaseTurnRate * GetWorld()->DeltaTimeSeconds);
}

void AMyCharacter::SufferDamage(float Damage) 
{
	if (!bReadyParry) 
	{
		float HealthRecent = Health - Damage;
		HealthRecent = FMath::Clamp(HealthRecent,0.f,MaxHealth);
		Health = HealthRecent;
		if (Health <= 0)
		{
			Died();
		}
	}
}

void AMyCharacter::SetStatus(EStatus Status) 
{
	CurrentStatus = Status;
	switch (CurrentStatus)
	{
	case EStatus::ES_Normal:
		GetCharacterMovement()->MaxWalkSpeed = RunSpeed;
		break;
	case EStatus::ES_Sprint:
		GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
		break;
	default:
		break;
	}
}

void AMyCharacter::BeginSprint() 
{
	if (!IsExhausted && !bAttacking && !bReadyParry)
	{
		SetStatus(EStatus::ES_Sprint);
	}
}

void AMyCharacter::EndSprint() 
{
	SetStatus(EStatus::ES_Normal);
}

void AMyCharacter::Died() 
{
	DisableInput(PlayerController);//禁止玩家输入
	UAnimInstance* Instance = GetMesh()->GetAnimInstance();
	if (AnimMontage && Instance)
	{
		Instance->Montage_Play(AnimMontage);
		Instance->Montage_JumpToSection(FName("Death"), AnimMontage);
	}
	CombatSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
	//取消胶囊提的碰撞
	GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);

}

//死亡通知
void AMyCharacter::DeathEnd()
{
	UAnimInstance* Instance = GetMesh()->GetAnimInstance();
	if (Instance && AnimMontage)
	{
		Instance->Montage_Pause();
		Destroy();
		//显示死亡UI
		if (PlayerController)
		{
			PlayerController->SetOverGameUIVisibility(true);
		}
	}
}

void AMyCharacter::SweapWeapon() 
{
	if (!bAttacking)
	{
		AWeapon* Weapon = Cast<AWeapon>(OverlapActor);
		if (Weapon)
		{
			Weapon->EquipWeapon(this);
			OverlapActor = nullptr;
		}
	}	
}

void AMyCharacter::SetSweapWeapon(class AWeapon* Weapon)
{
	if (EquipedWeapon) 
	{
		EquipedWeapon->Destroy();
	}
	EquipedWeapon = Weapon;
}

void AMyCharacter::OnClickBegin() 
{
	bClicking = true;
	if (!bAttacking && EquipedWeapon && !bReadyParry)
	{
		AttackBegin();
	}
}

void AMyCharacter::OnClickEnd() 
{
	bClicking = false;
}

void AMyCharacter::AttackBegin() 
{

	//攻击开始时，面向敌人(FindLookAtRotation)
	if (TargetEnemy)
	{
		FRotator CurrentRotator = GetActorRotation();
		/*
		UKismetMathLibrary::FindLookAtRotation(Param1,Param2)
		Param1:自己点位置
		Param2:目标点位置
		*/
		FRotator TargetRotator = UKismetMathLibrary::FindLookAtRotation(GetActorLocation(), TargetEnemy->GetActorLocation());
		TargetRotator = FRotator(CurrentRotator.Pitch, TargetRotator.Yaw, CurrentRotator.Roll);
		SetActorRotation(TargetRotator);
	}

	//当前角色的动画蓝图实例赋予Instance
	UAnimInstance* Instance = GetMesh()->GetAnimInstance();
	if (AnimMontage && Instance && !Instance->Montage_IsPlaying(AnimMontage))
	{
		bAttacking = true;
		Instance->Montage_Play(AnimMontage);
		switch (AttackNum)
		{
		case 1:
			Hurt = 10;
			Instance->Montage_JumpToSection(FName("AttackA"), AnimMontage);
			if (NiagaraComponent)
			{
				UNiagaraFunctionLibrary::SpawnSystemAtLocation(GetWorld(), NiagaraSystem, GetActorLocation());
			}
			AttackNum++;
			GetWorldTimerManager().SetTimer(RestTimer, this, &AMyCharacter::ClearTimer, 0.1f, false, 3.0f);
			break;
		case 2:
			Hurt = 30;
			Instance->Montage_JumpToSection(FName("AttackB"), AnimMontage);
			if (NiagaraComponent)
			{
				UNiagaraFunctionLibrary::SpawnSystemAtLocation(GetWorld(), NiagaraSystem, GetActorLocation());
			}
			AttackNum++;
			GetWorldTimerManager().ClearTimer(RestTimer);
			GetWorldTimerManager().SetTimer(RestTimer, this, &AMyCharacter::ClearTimer, 0.1f, false, 5.0f);
			break;
		case 3:
			Hurt = 50;
			Instance->Montage_JumpToSection(FName("AttackC"), AnimMontage);
			if (NiagaraComponent)
			{
				UNiagaraFunctionLibrary::SpawnSystemAtLocation(GetWorld(), NiagaraSystem, EquipedWeapon->Mesh->GetSocketLocation("End"));
			}
			GetWorldTimerManager().ClearTimer(RestTimer);
			AttackNum = 1;
		}
	}
}

void AMyCharacter::AttackEnd() 
{
	bAttacking = false;
	if (bClicking && EquipedWeapon)
	{
		AttackBegin();
	}
}

void AMyCharacter::ClearTimer() 
{
	AttackNum = 1;
}

void AMyCharacter::PickupMeat(MeatType Type,uint32 Count)
{
	switch (Type)
	{
	case Meat:
		MeatNum += Count;
		break;
	default:
		break;
	}
}

//攻击朝向
void AMyCharacter::CombatSphereOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp,
	int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	//记录进入攻击范围的敌人
	if (!TargetEnemy)
	{
		if (OtherActor)
		{
			AEnemy* Enemy = Cast<AEnemy>(OtherActor);
			if (Enemy)
			{
				TargetEnemy = Enemy;
				
				//当Enemy存在，并且存在PlayerController则显示UI
				if (PlayerController)
				{
					PlayerController->SetEnemyUIVisibility(true);
				}
			}
		}
	}
}

void AMyCharacter::CombatSphereOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp,
	int32 OtherBodyIndex)
{
	if (OtherActor)
	{
		AEnemy* Enemy = Cast<AEnemy>(OtherActor);
		if (Enemy == TargetEnemy)
		{
			TargetEnemy = nullptr;
			//在离开攻击范围设置EnemyHealthBar不可见
			if (PlayerController)
			{
				PlayerController->SetEnemyUIVisibility(false);
			}
		}
	}
}

void AMyCharacter::SaveGame()
{
	//创建保存游戏类的实例
	/*创建UGameplayStatics的对象使用StaticClass()转换为创建的UMySaveGame类的实例Instance*/
	UMySaveGame* Instance = Cast<UMySaveGame>(UGameplayStatics::CreateSaveGameObject(UMySaveGame::StaticClass()));
	if (Instance)
	{
		Instance->PlayerSlot.Health = Health;
		Instance->PlayerSlot.MaxHealth = MaxHealth;
		Instance->PlayerSlot.MaxStamina = MaxStamina;
		Instance->PlayerSlot.MeatNum = MeatNum;
		if (EquipedWeapon)
		{
			Instance->WeaponClass = EquipedWeapon->GetClass();
		}
		else
		{
			Instance->WeaponClass = nullptr;
		}
		//存档名为中文要设置编译器
		/*
		1.设置1：工具-自定义-命令-文件-添加命令-文件（添加高级保存选项）
		2.设置2：文件-高级保存选项-Unicode(UTF-8)
		*/
		UGameplayStatics::SaveGameToSlot(Instance, TEXT("游戏存档"), 0);
		UE_LOG(LogTemp,Warning,TEXT("Save Successfully"));
	}
}

void AMyCharacter::ReadGame()
{
	UMySaveGame* Instance = Cast<UMySaveGame>(UGameplayStatics::LoadGameFromSlot(TEXT("游戏存档"), 0));
	if (Instance)
	{
		Health = Instance->PlayerSlot.Health;
		MaxHealth = Instance->PlayerSlot.MaxHealth;
		MaxStamina = Instance->PlayerSlot.MaxStamina;
		MeatNum = Instance->PlayerSlot.MeatNum;
		if (Instance->WeaponClass)
		{
			UWorld* World = GetWorld();
			AWeapon* Weapon = Cast<AWeapon>(World->SpawnActor(Instance->WeaponClass));
			if (Weapon)
			{
				Weapon->EquipWeapon(this);
			}
		}
	}
}

void AMyCharacter::PauseGame()
{
	//在控制器里实现
	if (PlayerController)
	{
		PlayerController->TogglePause();
	}
}

void AMyCharacter::StartTrace()
{
	bEnableTrace = true;
	bReadyTrace = true;
	//设定检测的计时器
	GetWorldTimerManager().SetTimer(TraceTimer, this, &AMyCharacter::AttackTrace, 0.01f, true, 0.01f);
}

void AMyCharacter::EndTrace() 
{
	bEnableTrace = false;
	GetWorldTimerManager().ClearTimer(TraceTimer);
}

void AMyCharacter::AttackTrace() 
{
#if 8 /*Method 8*/
	if (EquipedWeapon && bEnableTrace && bReadyTrace)
	{	
		bReadyTrace = !bReadyTrace;

		FVector BeginLoc = EquipedWeapon->Mesh->GetSocketLocation("Start");
		FVector EndLoc = EquipedWeapon->Mesh->GetSocketLocation("End");
		//检测通道
		TArray<TEnumAsByte<EObjectTypeQuery> > ObjectTypes;
		ObjectTypes.Add(EObjectTypeQuery::ObjectTypeQuery3);

		TArray<AActor*> IgnoreActors;
		IgnoreActors.Add(this);

		TArray<FHitResult> HitResult;
	
		bTraced = UKismetSystemLibrary::SphereTraceMultiForObjects(GetWorld(), BeginLoc, EndLoc, 30.f, ObjectTypes, false, IgnoreActors,
			EDrawDebugTrace::None, HitResult, true);

		//伤害类型
		if (DamageTypeClass)
		{
			UE_LOG(LogTemp,Warning,TEXT("%f"),Hurt);
			//发送伤害
			//GetController伤害发起者的控制器
			UGameplayStatics::ApplyDamage(TargetEnemy, Hurt, GetController(), this, DamageTypeClass);
		}
	}
#endif
}

//接受伤害
float AMyCharacter::TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator,
	AActor* DamageCauser) 
{
	SufferDamage(DamageAmount);
	return DamageAmount;	
}

//Roll
void AMyCharacter::StartRoll() 
{
	bReadyRoll = true;
	UAnimInstance* Instance = GetMesh()->GetAnimInstance();
	if (RollMontage && Instance && !Instance->Montage_IsPlaying(RollMontage)) 
	{
		Instance->Montage_Play(RollMontage);
	}
}
//blueprintcallable
void AMyCharacter::EndRoll() 
{
	bReadyRoll = false;
	if (bReadyRoll)
	{
		StartRoll();
	}
}
//input
void AMyCharacter::ClickRollKey() 
{
	bClickRoll = true;
	if (!bAttacking) 
	{
		StartRoll();
	}
}
//input
void AMyCharacter::UnClickRollKey()
{
	bClickRoll = false;
}

//Parry
void AMyCharacter::ClickParryKeyBegin() 
{
	bClickParryKey = true;
	bReadyParry = true;
	if (!bAttacking && bReadyParry)
	{
		ParryStart();
	}
}

void AMyCharacter::ClickParryKeyEnd() 
{
	bClickParryKey = false;
}

void AMyCharacter::ParryEnd() 
{
	bReadyParry = false;
	if (bClickParryKey)
	{
		ParryStart();
	}
}

void AMyCharacter::ParryStart() 
{
	UAnimInstance* Instance = GetMesh()->GetAnimInstance();
	if (Instance && ParryMontage && !Instance->Montage_IsPlaying(ParryMontage))
	{
		Instance->Montage_Play(ParryMontage);
		Instance->Montage_JumpToSection(FName("Parry"), ParryMontage);
	}
}